package common

import (
	"errors"
	"fmt"
	"github.com/kataras/iris"
	"github.com/kataras/iris/mvc"
	"html/template"
	"math"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"
)

//静态文件生成函数
func Generate(tmpl *template.Template, outPath string, ctx iris.Context, data interface{}) bool {
	out_path := filepath.Join(outPath)
	outSource, err := os.Create(out_path)
	if err != nil {
		ctx.Application().Logger().Error("创建静态文件失败#1: ", err)
		return false
	}
	err = tmpl.Execute(outSource, data)
	if err != nil {
		ctx.Application().Logger().Error("创建静态文件失败#2: ", err)
		return false
	}
	return true
}

func Warn (msg string, goWhere string) mvc.View {
	return mvc.View{
		Name:   "shared/response.html",
		Data: iris.Map{
			"warn":     msg,
			"go": goWhere,
		},
	}
}

//根据结构体中sql标签映射数据到结构体中并且转换类型
func DataToStructByTagSql(data map[string]string, obj interface{}) {
	objValue := reflect.ValueOf(obj).Elem()
	for i := 0; i < objValue.NumField(); i++ {
		//获取sql对应的值
		value := data[objValue.Type().Field(i).Tag.Get("sql")] //map中的具体值
		//获取对应字段的名称
		name := objValue.Type().Field(i).Name
		//获取对应字段类型
		structFieldType := objValue.Field(i).Type()
		//获取变量类型，也可以直接写"string类型"
		val := reflect.ValueOf(value)
		var err error
		if structFieldType != val.Type() {
			//类型转换
			val, err = TypeConversion(value, structFieldType.Name()) //类型转换
			if err != nil {

			}
		}
		//设置类型值
		objValue.FieldByName(name).Set(val)
	}
}

//类型转换
func TypeConversion(value string, ntype string) (reflect.Value, error) {
	if ntype == "string" {
		return reflect.ValueOf(value), nil
	} else if ntype == "time.Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "Time" {
		t, err := time.ParseInLocation("2006-01-02 15:04:05", value, time.Local)
		return reflect.ValueOf(t), err
	} else if ntype == "int" {
		i, err := strconv.Atoi(value)
		return reflect.ValueOf(i), err
	} else if ntype == "int8" {
		i, err := strconv.ParseInt(value, 10, 8)
		return reflect.ValueOf(int8(i)), err
	} else if ntype == "int32" {
		i, err := strconv.ParseInt(value, 10, 32)
		return reflect.ValueOf(int32(i)), err
	} else if ntype == "int64" {
		i, err := strconv.ParseInt(value, 10, 64)
		return reflect.ValueOf(i), err
	} else if ntype == "float32" {
		i, err := strconv.ParseFloat(value, 32)
		return reflect.ValueOf(float32(i)), err
	} else if ntype == "float64" {
		i, err := strconv.ParseFloat(value, 64)
		return reflect.ValueOf(i), err
	}

	//else if .......增加其他一些类型的转换

	return reflect.ValueOf(value), errors.New("未知的类型：" + ntype)
}

//记录日志到文件
func Log2File(msg ...string) {
	//给字符串添加时间和换行符
	sliceMsg := append(msg, " | ", time.Now().Format("2006-01-02 15:04:05"), "\n")
	s := strings.Join(sliceMsg, " ")
	file, err := os.OpenFile("./error.log", os.O_APPEND|os.O_RDWR|os.O_CREATE, 0777)
	if err != nil {
		fmt.Println("打开文件失败: ", err)
	}
	defer file.Close()
	fmt.Fprintln(file, s)
}

//获取本地ip
func GetIntranceIp() {
	ifaces, err := net.Interfaces()
	if err != nil {
		fmt.Println(err)
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			fmt.Println(err)
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				continue // not an ipv4 address
			}
			fmt.Println("ip: ", ip.String(), "mac: ", iface.HardwareAddr.String())
		}
	}
}


func GetCurrentPath() (string, error) {
	file, err := exec.LookPath(os.Args[0])
	if err != nil {
		return "", err
	}
	path, err := filepath.Abs(file)
	if err != nil {
		return "", err
	}
	i := strings.LastIndex(path, "/")
	if i < 0 {
		i = strings.LastIndex(path, "\\")
	}
	if i < 0 {
		return "", errors.New(`error: Can't find "/" or "\".`)
	}
	return string(path[0 : i+1]), nil
}

//salt值用来微调，防止随机串被预测
func PseudoEncrypt8(val int32, salt int32) int32 {
	if salt == 0 {
		salt = 150889
	}
	var l1 int32 = (val >> 13) & 8191
	var r1 int32 = val & 8191
	var l2, r2 int32
	var i int

	for i = 0; i < 3; i++ {
		l2 = r1
		r2 = l1 ^ int32(math.Round(float64((1366*r1+salt)%714025)/714025.0*8191))
		l1 = l2
		r1 = r2
	}

	return ((r1 << 13) + l1)
}